#include "stlslicewidget.h"

STLSliceWidget::STLSliceWidget(std::shared_ptr<STLReader> stl_reader, const SliceInfo& slice_info, QWidget *parent)
{
    m_sp_stl_reader = stl_reader;
    assert(slice_info.plane_norm != QVector3D());
    m_plane_norm = slice_info.plane_norm;
    m_camera_dis = slice_info.distance;
    m_stride = slice_info.thickness;
    m_p_program = new QOpenGLShaderProgram(this);
    m_sp_model_edges_convertor = std::make_shared<EdgesConvertor>();
}

STLSliceWidget::~STLSliceWidget()
{
    delete m_p_program;
}

QVector3D STLSliceWidget::getCurrentCameraPos()
{
    return m_camera.getEye();
}

float STLSliceWidget::getCurrentSliceDepth()
{
    return m_cur_slice_lambda;
}

void STLSliceWidget::wheelEvent(QWheelEvent *event)
{
    updateSliceIndexRangeInfo(m_sliceHead, m_slicePointsCount, event->angleDelta().y() > 0.0f ? true : false);
    update();
}

void STLSliceWidget::initializeGL()
{
    initializeOpenGLFunctions();
    glEnable(GL_DEPTH_TEST);
    glClearColor(0.3f, 0.5f, 1.0f, 1.0f);
    // glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

    // 初始化摄像机
    // TODO（mengjinlang）: 在没搞明白为什么切片视角，坐标系视角和模型视角为什么统一了的数学原理前，别改动这个相机初始化参数
    m_camera.setPerspective(static_cast<float>(width()) / static_cast<float>(height()), 0.1f, 100.0f);
    m_camera.setMouseLockedStatus(true);
    QVector3D up(0.0f, 1.0f, 0.0f);
    if (qFuzzyCompare(qAbs(QVector3D::dotProduct(m_plane_norm.normalized(), up)), 1.0f))
        up = QVector3D(1.0f, 0.0f, 0.0f);
    m_camera.lookAt(m_camera_dis * m_plane_norm, QVector3D(0, 0, 0), up);

    // 编译和链接着色器
    if(!compileAndLinkShaders(m_p_program, ":/vertex_shaders/shaders_code/vs/vertex_shader_android.vs", ":/fragment_shaders/shaders_code/fs/fragment_shader_android.fs"))
    {
        qDebug() << "Failed to compile and link shaders.";
        return;
    }

    // 将模型拉到视野中心
    calAndSetModelInitPhase();

    /* 边数据 */
    // meshes 转 edges
    m_sp_model_edges_convertor->convertFrom(m_sp_stl_reader->getModelMeshes());

    // 获取edges数据
    QVariant model_data = m_sp_model_edges_convertor->getModel();
    QVector<Edge> model_edges = model_data.value<QVector<Edge>>();

    /* 边数据切片 */
    // 预处理边向量区间
    auto preprocessed_partitions = m_stl_model_slices_producer.preprocessModelDataAlongDirection(model_edges, m_plane_norm);

    // 求面片与平面的所有交点
    QVector<Vertex> intersectedPoints;
    m_stl_model_slices_producer.generateShownVertices(preprocessed_partitions, m_plane_norm, m_stride, intersectedPoints, m_intersected_points_counts, m_slice_lambdas);

    setSliceShaderProgramDataFormation();

    setSliceVertexData2Shader(intersectedPoints);
}

void STLSliceWidget::paintGL()
{
    glViewport(0, 0, this->width(), this->height());
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 清除颜色和深度缓冲
    m_vao.bind();
    m_p_program->bind();

    m_p_program->setUniformValue("model", m_model);
    m_p_program->setUniformValue("view", m_camera.getViewMatrix());
    m_p_program->setUniformValue("projection", m_camera.getProjectionMatrix());

    // 设置法线矩阵
    QMatrix3x3 normModelMatrix = m_model.normalMatrix();
    m_p_program->setUniformValue("normModelMatrix", normModelMatrix);

    glDrawArrays(GL_POINTS, m_sliceHead, m_slicePointsCount);
    m_p_program->release();
    m_vao.release();
}

void STLSliceWidget::resizeGL(int w, int h)
{
    glViewport(0, 0, w, h);
}

bool STLSliceWidget::loadShaderSource(const QString &filePath, QString &source)
{
    QFile file(filePath);
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
    {
        qDebug() << "Failed to open shader file:" << filePath;
        return false;
    }

    QTextStream stream(&file);
    source = stream.readAll();
    file.close();
    return true;
}

bool STLSliceWidget::compileAndLinkShaders(QOpenGLShaderProgram *p_program, const QString& vs_file_path, const QString& fs_file_path)
{
    if(p_program == nullptr)
        return false;

    QString vertexSource, fragmentSource;

    // 加载顶点着色器代码
    if (!loadShaderSource(vs_file_path, vertexSource))
    {
        return false;
    }

    // 加载片段着色器代码
    if (!loadShaderSource(fs_file_path, fragmentSource))
    {
        return false;
    }

    // 编译顶点着色器
    if (!p_program->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexSource))
    {
        qDebug() << "Vertex shader compile error:" << p_program->log();
        return false;
    }

    // 编译片段着色器
    if (!p_program->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentSource))
    {
        qDebug() << "Fragment shader compile error:" << p_program->log();
        return false;
    }

    // 链接着色器程序
    if (!p_program->link())
    {
        qDebug() << "Shader program link error:" << p_program->log();
        return false;
    }

    return true;
}

void STLSliceWidget::setSliceShaderProgramDataFormation()
{
    if(m_p_program == nullptr)
    {
        qDebug()<<"Shader promgram has not been initialized. Abort building shader program.";
        return;
    }
    m_vao.create();
    m_vao.bind();

    m_vbo.create();
    m_vbo.bind();

    m_p_program->bind();
    m_p_program->enableAttributeArray(0);
    m_p_program->setAttributeBuffer(0, GL_FLOAT, offsetof(Vertex, pos), 3, sizeof(Vertex));

    m_p_program->enableAttributeArray(1);
    m_p_program->setAttributeBuffer(1, GL_FLOAT, offsetof(Vertex, norm), 3, sizeof(Vertex));

    m_p_program->enableAttributeArray(2);
    m_p_program->setAttributeBuffer(2, GL_FLOAT, offsetof(Vertex, color), 4, sizeof(Vertex));

    m_vao.release();
    m_vbo.release();
    m_p_program->release();
}

void STLSliceWidget::setSliceVertexData2Shader(const QVector<Vertex>& slice_vertices)
{
    m_vbo.bind();
    assert(slice_vertices.size() * sizeof(Vertex) <= INT_MAX);
    m_vbo.allocate(slice_vertices.constData(), slice_vertices.size() * sizeof(Vertex)); // 如果顶点数量大于int的最大值，此处会异常
    m_vbo.release();
}

void STLSliceWidget::updateSliceIndexRangeInfo(GLint &slice_head, GLint &count, bool increaseOrDecrease)
{
    if(increaseOrDecrease && m_curShowSliceIndice + 1 <= m_intersected_points_counts.size())
    {
        m_cur_slice_lambda = m_slice_lambdas[m_curShowSliceIndice];
        count = m_intersected_points_counts[m_curShowSliceIndice];
        slice_head += m_curShowSliceIndice == 0 ? -slice_head : m_intersected_points_counts[m_curShowSliceIndice - 1];
        m_curShowSliceIndice++;
        qDebug() << "当前轮廓有：" << count << "个点";
    }
    else if(!increaseOrDecrease && m_curShowSliceIndice > 0)
    {
        m_cur_slice_lambda = m_slice_lambdas[m_curShowSliceIndice - 2];
        count = slice_head == 0 ? 0 : m_intersected_points_counts[m_curShowSliceIndice - 2];
        slice_head -= slice_head == 0 ? slice_head : m_intersected_points_counts[m_curShowSliceIndice - 2];
        m_curShowSliceIndice--;
        qDebug() << "当前轮廓有：" << count << "个点";
    }
}

void STLSliceWidget::calAndSetModelInitPhase(bool isFlip)
{
    // 计算与设置STL模型初始相位
    m_centroid = m_model_attr_calculator.calModelCentroid(m_sp_stl_reader->getModelMeshes());
    m_model.translate(-m_centroid.x(), -m_centroid.y(), -m_centroid.z());
}
